package net.zoneland.knowledge.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import net.zoneland.knowledge.config.TalkConfig;
import net.zoneland.knowledge.constant.ApproveConstant;
import net.zoneland.knowledge.constant.BizConstant;
import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.dao.WorkApplyInfoMapper;
import net.zoneland.knowledge.enums.AttachTypeEnum;
import net.zoneland.knowledge.enums.WorkStatusEnum;
import net.zoneland.knowledge.exceptions.WorkApplyException;
import net.zoneland.knowledge.model.*;
import net.zoneland.knowledge.service.*;
import net.zoneland.knowledge.service.bean.ResultInfo;
import net.zoneland.knowledge.utils.WorkApplyOperationUtils;
import net.zoneland.uniflow.client.TaskObject;
import net.zoneland.uniflow.client.UserInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class SecretaryProjectServiceImpl implements SecretaryProjectService {

    /**
     * 日志.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SecretaryProjectServiceImpl.class);

    /**
     * 工单对象json的参数名.
     */
    private static final String WORK_APPLY_INFO_JSON_NAME = "workApplyInfo";

    /**
     * 决策选项信息.
     */
    private static final String DECISION_SELECTED = "decisionSelected";

    /**
     * 查询参数信息.
     */
    private static final String QUERY_PARAMS = "queryParam";

    /**
     * 工单id的参数名.
     */
    private static final String WORK_APPLY_ID_NAME = "workApplyId";

    /**
     * 工单信息id不能为空提示语.
     */
    private static final String WORK_APPLY_ID_CAN_NOT_EMPTY = "工单信息id不能为空！";

    /**
     * 工单不存在提示语.
     */
    private static final String APPLY_NOT_EXIST = "当前工单不存在！id:";

    /**
     * 工单数据格式异常提示语.
     */
    private static final String APPLY_DATA_FORMAT_EXCEPTION = "工单数据格式异常，请检查相应的数据是否完整！";

    /**
     * 流程节点id。
     */
    private static final String DRAFT = "draft";
    private static final String PARTY_SECRETARY_APPROVE = "partySecretaryApprove";

    /**
     * 任务id的参数名.
     */
    private static final String TASK_ID_NAME = "taskId";

    /**
     * 附件的参数名.
     */
    private static final String FILE_NAME = "file";

    /**
     * 工单信息服务接口
     */
    @Resource
    private WorkApplyInfoService workApplyInfoService;

    /**
     * 已办状态表服务.
     */
    @Resource
    private WorkDoneStatusInfoService workDoneStatusInfoService;

    /**
     * 发送邮件服务接口.
     */
    @Resource
    private EmailService<TaskObject[], WorkApplyInfo> emailService;

    /**
     * 通知服务接口.
     */
    @Resource
    private NotifyService notifyService;

    /**
     * 配置服务接口.
     */
    @Resource
    private TalkConfig talkConfig;

    /**
     * json工具类.
     */
    @Resource
    protected ObjectMapper objectMapper;

    /**
     * 工单信息数据接口.
     */
    @Resource
    private WorkApplyInfoMapper workApplyInfoMapper;

    /**
     * 流程业务接口.
     */
    @Resource
    private UniFlowService uniFlowService;

    /**
     * 保存草稿.
     *
     * @param info 请求
     * @param user 用户
     */
    @Override
    public void saveDraft(final WorkApplyInfo info, final LoginUserPojo user) {

        try {
            Preconditions.checkNotNull(info, ApproveConstant.WORK_APPLY_INFO_NOT_EMPTY);
            if (StringUtils.isBlank(info.getId())) {
                insertSecretaryProject(info, user);
            } else {
                updateSecretaryProject(info);
            }
            // 起草保存也要更新当前工单状态，但不需要加已办记录，这个要特殊处理
            workDoneStatusInfoService.saveDraftWorkApplyDoneStatus(info, user);
        } catch (final Exception e) {
            throw new WorkApplyException("保存工单失败", e);
        }

    }

    /**
     * 提交草稿工单.
     *
     * @param info              工单信息
     * @param processProcessing 流程处理信息
     * @param user              当前用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitDraft(final WorkApplyInfo info, final ProcessProcessing processProcessing, final LoginUserPojo user) {
        try {
            Preconditions.checkNotNull(info, ApproveConstant.WORK_APPLY_INFO_NOT_EMPTY);
            Preconditions.checkNotNull(user, ApproveConstant.WORK_USER_NOT_EMPTY);
            Preconditions.checkArgument(StringUtils.isBlank(info.getCreatorUid())
                    || StringUtils.equals(user.getUserID(), info.getCreatorUid()),
                ApproveConstant.CURRENT_USER_NOT_SPONSOR);
            Preconditions.checkArgument(info.getStatus() == null || info.getStatus() == WorkStatusEnum.DRAFT,
                ApproveConstant.CURRENT_APPLY_NOT_DRAFT);
            info.setCreatorPartyOrgUuid(user.getPartyOrgUuid());

            // 设置下一节点信息
            final UserInfo[] nextHandlers = processProcessing.getNextHandlers();
            info.setHandlerUid(Stream.of(nextHandlers).map(UserInfo::getUserId).collect(Collectors.joining(";")));
            info.setHandlerName(Stream.of(nextHandlers).map(UserInfo::getUserName).collect(Collectors.joining(";")));
            info.setStatus(processProcessing.getNextSubmitStatus());

            //先保存工单
            this.saveDraft(info, user);
            // 流程扩展参数设置
            Map<String, String> extendData = new HashMap<>();
            workApplyInfoMapper.insertWorkApproveOpinion(WorkApplyOperationUtils.buildApproveOpinionWithNodeId(processProcessing.getOpinion(),
                user, info,
                ApproveConstant.DEFAULT_SUBMIT_NAME,
                WorkStatusEnum.DRAFT.getValue()));

            final TaskObject[] tasks = WorkApplyOperationUtils.queryTask(uniFlowService, user.getUserID(), info,
                new String[]{WorkStatusEnum.DRAFT.getValue()},
                WorkApplyOperationUtils.READY_STATUS);
            String taskIdTmp = ArrayUtils.isNotEmpty(tasks) ? tasks[0].getTiid() : null;
            ResultInfo resultInfo;
            if (StringUtils.isNotBlank(taskIdTmp)) {
                // 有可能是退回拟稿后再次提交，这时需要调用完成任务
                resultInfo = uniFlowService.completeTask(taskIdTmp, WorkApplyOperationUtils.convertUserInfo(user),
                    processProcessing.getDecision(), processProcessing.getOption(),
                    processProcessing.getOpinion(), processProcessing.getDestNodeId(),
                    nextHandlers,
                    extendData, null);
            } else {
                resultInfo = uniFlowService.startAndJumpTask(info.getFlowType(),
                    new UserInfo(user.getUserID(), user.getUserName()),
                    info.getId(),
                    info.getTitle(), processProcessing.getDestNodeId(),
                    nextHandlers, extendData);
            }
            Preconditions.checkNotNull(resultInfo, ApproveConstant.DRAFT_ERROR_MESSAGE);
            Preconditions.checkArgument(resultInfo.isResult(), resultInfo.getInfo());

            // 流程启动成功后，更新业务工单的piid和applyId，便于关联业务信息和流程信息
            this.updateWorkApplyIdAndPiid(info, (TaskObject[]) resultInfo.getData());

            workDoneStatusInfoService.dealChapterApplyDoneStatus(info, user);
            workApplyInfoMapper.updateWorkApplyInfo(info);
            emailService.sendEmail(this::sendEmailFunction, (TaskObject[]) resultInfo.getData(), info);
        } catch (final Exception e) {
            throw new WorkApplyException("提交草稿工单失败！taskId:" + processProcessing.getTaskId(), e);
        }
    }

    /**
     * 初始化书记项目工单信息
     *
     * @param request 请求
     * @param user    当前用户
     * @return WorkApplyInfo 工单信息
     */
    @Override
    public WorkApplyInfo initSecretaryProject(HttpServletRequest request, LoginUserPojo user) {

        WorkApplyInfo info = new WorkApplyInfo();
        try {
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            // info.setFlowType(BizConstant.PARTY_BRANCH_PROCESS_CODE);

            // 附件
            final MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest) request;

            final List<WorkApplyInfoAttachment> newUploadAttachments = getAttachments(multipartReq,
                FILE_NAME, user);
            if (CollectionUtils.isNotEmpty(newUploadAttachments)) {
                final List<WorkApplyInfoAttachment> attachments = ObjectUtils.defaultIfNull(info.getAttachments(), Lists.newArrayList());
                attachments.addAll(newUploadAttachments);
                info.setAttachments(attachments);
            }
        } catch (final Exception e) {
            LOGGER.error("初始化工单出错, 工单标题：{}", info.getTitle());
            throw new WorkApplyException(APPLY_DATA_FORMAT_EXCEPTION, e);
        }
        return info;
    }

    /**
     * 更新工单的ApplyId.
     *
     * @param info 工单
     * @param data 流程引擎返回的节点任务信息
     */
    private void updateWorkApplyIdAndPiid(final WorkApplyInfo info, final TaskObject[] data) {
        if (data.length > 0) {
            // 不管返回多少个任务都是同一个流程下的，因此此处只取一个任务的工单id和piid的即可
            TaskObject taskObject = data[0];
            info.setApplyId(taskObject.getApplyId());
            info.setPiid(taskObject.getPiid());
            this.workApplyInfoMapper.updateWorkApplyInfo(info);
        }
    }

    /**
     * 初始化流程处理信息
     *
     * @param request 请求
     * @param user    当前用户
     * @return ProcessProcessing 流程处理信息
     */
    @Override
    public ProcessProcessing initProcessProcessing(HttpServletRequest request, LoginUserPojo user) {
        ProcessProcessing processProcessing = new ProcessProcessing();
        try {

            // 获取流程处理信息
            final String processing = request.getParameter(DECISION_SELECTED);
            processProcessing = objectMapper.readValue(processing, ProcessProcessing.class);

            // 获取当前处理信息
            final WorkApplyInfo info;
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            if (StringUtils.isBlank(workApplyInfo)) {
                final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
                Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
                info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            } else {
                info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            }
            final UserInfo currentHandler = new UserInfo();
            currentHandler.setUserId(info.getCurrentHandlerUid());
            currentHandler.setUserName(info.getCurrentHandlerName());
            processProcessing.setCurrentHandler(currentHandler);

            // 设置下一处理状态信息
            processProcessing.setNextSubmitStatus(WorkStatusEnum.getByValue(processProcessing.getDestNodeId()));

            // 获取下一处理人信息
            final String nextHandlerName = processProcessing.getNextHandlerName();
            UserInfo[] nextHandlers = WorkApplyOperationUtils.getNextHandlers(nextHandlerName);
            processProcessing.setNextHandlers(nextHandlers);

        } catch (final Exception e) {
            LOGGER.error("初始化流程处理信息出错, 流程信息：{}", processProcessing);
            throw new WorkApplyException(APPLY_DATA_FORMAT_EXCEPTION, e);
        }
        return processProcessing;
    }

    /**
     * 解析人员
     *
     * @param orgUserSelectItems 选人框结果
     * @return 人员对象
     */
    @Override
    public List<PartyUserVo> getResolveNextHandler(final List<PartyOrgUserSelectItem> orgUserSelectItems) {
        return orgUserSelectItems.stream().map(item -> new PartyUserVo(item.getId(), item.getLabel(), item.getOrgShortName())).collect(Collectors.toList());
    }

    /**
     * 验证选人框参数.
     * 检查人员是否有oa账号
     *
     * @param users 解析后人员
     * @return 检证结果信息，非空：验证失败
     */
    @Override
    public String checkNextHandler(final List<PartyUserVo> users) {

        final StringBuilder msg = new StringBuilder();

        for (PartyUserVo user : users) {
            if (StringUtils.isBlank(user.getUid())) {
                msg.append(user.getOrgShortName()).append("的党员").append(user.getName()).append("员工帐号不存在，请先联系省公司党办完成该配置");
            }
        }

        return msg.toString();
    }

    /**
     * 提交工单信息.
     *
     * @param info              工单信息
     * @param processProcessing 流程处理信息
     * @param user              当前用户
     */
    @Override
    public void submit(final WorkApplyInfo info, final ProcessProcessing processProcessing, final LoginUserPojo user) {

        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(info.getTiid()), WorkApplyInfoService.TASK_ID_BLANK_ERROR_MSG);
            Preconditions.checkArgument(StringUtils.isNotBlank(info.getHandlerUid())
                    && StringUtils.equals(user.getUserID(), info.getHandlerUid()),
                ApproveConstant.CURRENT_USER_NOT_HANDLER);
            info.setUpdateTime(DateTime.now().toDate());
            final WorkStatusEnum currentStatus = info.getStatus();

            // 设置下一节点信息
            final WorkStatusEnum nextSubmitStatus = processProcessing.getNextSubmitStatus();
            final UserInfo[] nextHandlers = processProcessing.getNextHandlers();
            info.setHandlerUid(Stream.of(nextHandlers).map(UserInfo::getUserId).collect(Collectors.joining(";")));
            info.setHandlerName(Stream.of(nextHandlers).map(UserInfo::getUserName).collect(Collectors.joining(";")));
            info.setStatus(nextSubmitStatus);

            this.workApplyInfoMapper.updateWorkApplyInfo(info);
            final WorkApproveOpinion workApproveOpinion = WorkApplyOperationUtils.buildApproveOpinionWithNodeId(processProcessing.getOpinion(), user,
                info, processProcessing.getOption(),
                currentStatus.getValue());
            this.workApplyInfoMapper.insertWorkApproveOpinion(workApproveOpinion);
            final String taskId = info.getTiid();
            final ResultInfo resultInfo = this.uniFlowService.completeTask(taskId, WorkApplyOperationUtils.convertUserInfo(user),
                processProcessing.getDecision(),
                processProcessing.getOption(),
                processProcessing.getOpinion(), processProcessing.getDestNodeId(), nextHandlers, null, null);

            workDoneStatusInfoService.dealChapterApplyDoneStatus(info, user);
            //如果审批结束需要发送邮件给发起人
            if (nextSubmitStatus == WorkStatusEnum.END) {
                emailService.sendEmail(this::sendFinishEmailFunction, (TaskObject[]) resultInfo.getData(), info);

            } else {
                emailService.sendEmail(this::sendEmailFunction, (TaskObject[]) resultInfo.getData(), info);
            }
        } catch (final Exception e) {
            throw new WorkApplyException("提交工单失败！" + e.getMessage() + "taskId:" + info.getTiid(), e);
        }

    }

    /**
     * 获取书记项目工单信息
     *
     * @param request 请求
     * @param user    当前用户
     * @return WorkApplyInfo 工单信息
     */
    @Override
    public WorkApplyInfo getSecretaryProjectInfo(final HttpServletRequest request, final LoginUserPojo user) {
        // 获取工单信息
        final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
        final String taskId = request.getParameter(TASK_ID_NAME);
        Preconditions.checkArgument(StringUtils.isNotBlank(taskId), ApproveConstant.TASK_ID_BLANK_ERROR_MSG);

        final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
        info.setTiid(taskId);
        Preconditions.checkNotNull(info, APPLY_NOT_EXIST + workApplyId);
        return info;
    }

    /**
     * 获取下一节点信息.
     *
     * @param request 请求
     * @return 下一节点信息
     */
    @Override
    public NextNodeInfo queryNextNodeInfo(HttpServletRequest request) {

        final NextNodeInfo nextNodeInfo = new NextNodeInfo();
        try {

            final String decision = request.getParameter(DECISION_SELECTED);
            final ProcessProcessing processProcessing = objectMapper.readValue(decision, ProcessProcessing.class);
            final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
            final WorkApplyInfo info;
            if (StringUtils.isEmpty(workApplyId)) {
                final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
                info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            } else {
                info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            }

            final String nodeId = processProcessing.getDestNodeId();
            switch (nodeId) {
                case DRAFT:
                    final UserInfo userInfo = new UserInfo();
                    userInfo.setUserId(info.getCreatorUid());
                    userInfo.setUserName(info.getCreatorName());
                    userInfo.setDeptId(info.getCreatorDeptDn());
                    userInfo.setDeptName(info.getCreatorPartyOrgName());
                    nextNodeInfo.setUserInfo(userInfo);
                    nextNodeInfo.setNodeId(processProcessing.getDestNodeId());
                    nextNodeInfo.setNodeName(processProcessing.getDestNodeName());
                    break;
                case PARTY_SECRETARY_APPROVE:
                    nextNodeInfo.setUserInfo(WorkStatusEnum.PARTY_SECRETARY_APPROVE.getHandler(info));
                    nextNodeInfo.setNodeId(processProcessing.getDestNodeId());
                    nextNodeInfo.setNodeName(processProcessing.getDestNodeName());
                    break;
                default:
                    nextNodeInfo.setNodeId(processProcessing.getDestNodeId());
                    nextNodeInfo.setNodeName(processProcessing.getDestNodeName());
                    nextNodeInfo.setUserInfo(null);
            }

        } catch (final Exception e) {
            LOGGER.error("获取下一节点信息出错, 下一节点信息：{}", nextNodeInfo);
            throw new WorkApplyException(APPLY_DATA_FORMAT_EXCEPTION, e);
        }
        return nextNodeInfo;
    }

    /**
     * 查询业务流程信息.
     *
     * @param request 请求
     * @return ResultInfo 业务流程信息
     */
    @Override
    public ResultInfo queryBusinessProcess(HttpServletRequest request) {
        QueryParams queryParam = new QueryParams();
        try {
            // 获取查询参数信息
            final String queryParams = request.getParameter(QUERY_PARAMS);
            queryParam = objectMapper.readValue(queryParams, QueryParams.class);

            return uniFlowService.queryBusinessProcess(queryParam.getSystemId(), queryParam.getTemplateId(), queryParam.getBusinessProcess(), queryParam.getBizCategory(), queryParam.getRoles(), queryParam.getKeywords(), queryParam.getProvinceId());
        } catch (final Exception e) {
            LOGGER.error("初始化查询参数出错, 查询参数信息：{}", queryParam);
            throw new WorkApplyException("查询参数格式异常，请检查相应的数据是否完整！", e);
        }
    }

    /**
     * 获取决策信息.
     *
     * @param request 请求
     * @return ResponseResult<Object> 决策信息
     */
    @Override
    public ResultInfo queryTaskDecisions(final HttpServletRequest request) {
        QueryParams queryParam = new QueryParams();
        try {
            // 获取查询参数信息
            final String queryParams = request.getParameter(QUERY_PARAMS);
            queryParam = objectMapper.readValue(queryParams, QueryParams.class);

            final String systemId = queryParam.getSystemId();
            final String businessProcess = queryParam.getBusinessProcess();
            final String nodeId = queryParam.getNodeId();
            return uniFlowService.queryTaskDecisions(systemId, businessProcess, nodeId);
        } catch (final Exception e) {
            LOGGER.error("初始化查询参数出错, 查询参数信息：{}", queryParam);
            throw new WorkApplyException("查询参数格式异常，请检查相应的数据是否完整！", e);
        }

    }


    /**
     * 插入工单.
     *
     * @param info 工单
     * @param user 当前用户
     */
    private void insertSecretaryProject(final WorkApplyInfo info, final LoginUserPojo user) {
        info.setId(UUID.randomUUID().toString());
        final Date now = DateTime.now().toDate();
        if (info.getCreateTime() == null) {
            info.setCreateTime(now);
        }
        info.setMainWorkApplyId(info.getId());
        info.setApplyId(info.getId());
        info.setUpdateTime(now);
        info.setCreatorUid(user.getUserID());
        info.setCreatorName(user.getUserName());
        if (info.getStatus() == null) {
            info.setStatus(WorkStatusEnum.DRAFT);
        }

        workApplyInfoMapper.insertWorkApplyInfo(info);
        insertDraftAttachments(info);
        if (CollectionUtils.isNotEmpty(info.getWorkApproveOpinions())) {
            for (final WorkApproveOpinion opinion : info.getWorkApproveOpinions()) {
                opinion.setId(UUID.randomUUID().toString());
                opinion.setWorkApplyId(info.getId());
                if (opinion.getApproveTime() == null) {
                    opinion.setApproveTime(now);
                }
                workApplyInfoMapper.insertWorkApproveOpinion(opinion);
            }
        }
    }

    /**
     * 更新工单.
     *
     * @param info 工单
     */
    private void updateSecretaryProject(WorkApplyInfo info) {

        final WorkApplyInfo oldApplyInfo = workApplyInfoService.findWorkApplyInfoById(info.getId());
        Preconditions.checkNotNull(oldApplyInfo, APPLY_NOT_EXIST);
        info.setUpdateTime(DateTime.now().toDate());
        workApplyInfoMapper.updateWorkApplyInfo(info);
        //附件附件更新
        updateAttachments(info, oldApplyInfo);
    }

    /**
     * 插入附件.
     *
     * @param applyInfo 工单
     */
    private void insertDraftAttachments(final WorkApplyInfo applyInfo) {

        if (CollectionUtils.isNotEmpty(applyInfo.getAttachments())) {
            for (final WorkApplyInfoAttachment attachment : applyInfo.getAttachments()) {
                attachment.setId(UUID.randomUUID()
                    .toString());
                attachment.setWorkApplyId(applyInfo.getId());
                workApplyInfoMapper.insertWorkApplyInfoAttachment(attachment);
            }
        }
    }

    /**
     * 更新附件.
     *
     * @param info         工单
     * @param oldApplyInfo 旧工单
     */
    private void updateAttachments(final WorkApplyInfo info, final WorkApplyInfo oldApplyInfo) {
        // 附件更新
        final List<WorkApplyInfoAttachment> newAttaches = info.getAttachments();
        if (CollectionUtils.isNotEmpty(newAttaches)) {
            final List<WorkApplyInfoAttachment> newInsertAttaches = newAttaches
                .stream()
                .filter(it -> StringUtils.isBlank(it.getId()))
                .collect(Collectors.toList());
            for (final WorkApplyInfoAttachment attachment : newInsertAttaches) {
                attachment.setId(UUID.randomUUID()
                    .toString());
                attachment.setWorkApplyId(info.getId());
                workApplyInfoMapper.insertWorkApplyInfoAttachment(attachment);
            }
        }
        // 附件检查删除
        final List<WorkApplyInfoAttachment> oldAttaches = oldApplyInfo.getAttachments();
        if (CollectionUtils.isNotEmpty(oldAttaches)) {
            final Set<String> subAttachmentIds = Optional.ofNullable(newAttaches)
                .map(it -> it.stream()
                    .map(WorkApplyInfoAttachment::getId)
                    .collect(Collectors.toSet()))
                .orElseGet(Collections::emptySet);
            oldAttaches.stream()
                .filter(it -> !subAttachmentIds.contains(it.getId()))
                .forEach(x -> workApplyInfoMapper.deleteAttachmentById(x.getId()));
        }
    }

    /**
     * 获取附件对象集合.
     *
     * @param request   http请求
     * @param paramName 参数名
     * @param user      用户对象
     * @return 附件对象集合
     */
    private List<WorkApplyInfoAttachment> getAttachments(final MultipartHttpServletRequest request,
                                                         final String paramName, final LoginUserPojo user) {
        final List<MultipartFile> fileRes = request.getFiles(paramName);
        if (CollectionUtils.isNotEmpty(fileRes)) {
            return fileRes.stream().filter(it -> !it.isEmpty()).map(it -> {
                try {
                    return getWorkApplyInfoAttachment(user, it);
                } catch (IOException e) {
                    LOGGER.error("获取文件流失败，文件名称:{}", it.getOriginalFilename(), e);
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    private WorkApplyInfoAttachment getWorkApplyInfoAttachment(LoginUserPojo user, MultipartFile it) throws IOException {
        final WorkApplyInfoAttachment attachment = new WorkApplyInfoAttachment();
        attachment.setAttachment(it.getBytes());
        attachment.setUploadTime(DateTime.now().toDate());
        attachment.setType(AttachTypeEnum.SECRETARY_FILE);
        attachment.setUploaderUid(user.getUserID());
        attachment.setUploaderName(user.getUserName());
        attachment.setFileName(it.getOriginalFilename());
        return attachment;
    }

    /**
     * 构建审批结束发送邮件.
     *
     * @param map 参数.
     */
    private void sendFinishEmailFunction(final Map<String, Object> map) {
        final String emailLeader = (String) map.get(Constants.EMAIL_LEADER);
        final WorkApplyInfo workApplyInfo = (WorkApplyInfo) map.get(ApproveConstant.WORK_APPLY_INFO);
        final String title = MessageFormat.format(BizConstant.TITLE_TEMPLATE_FINISH, new String[]{workApplyInfo.getTitle()});
        final UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromUriString(talkConfig.getSiteUrl()).
            path("api/secretary/downloadApproveForm").
            queryParam(ApproveConstant.WORK_APPLY_ID_NAME, workApplyInfo.getId());
        final String content = MessageFormat.format(BizConstant.CONTENT_TEMPLATE_FINISH,
            new String[]{urlBuilder.toUriString(), workApplyInfo.getTitle()});
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("sendFinishEmailFunction mail title:{},mail receiver:{},mail content:{}", title, workApplyInfo.getCreatorUid(), content);
        }
        notifyService.notifyByEmail(new String[]{StringUtils.isNotBlank(emailLeader) ? emailLeader : workApplyInfo.getCreatorUid()}, title, content);
    }

    /**
     * 邮件发送.
     * 格式
     * 标题：党建机器人-书记项目立项申请流程：工单标题
     * 内容：
     * 请处理党建机器人-书记项目立项申请流程任务工单：工单标题。
     * 发起人：张三
     * 发起时间：2023-04-11 10:33
     *
     * @param map 邮件相关参数
     *            key emailLeader: 邮件接收者
     *            key param: uniflow TaskObject
     *            key workApplyInfo:工单实例
     */
    private void sendEmailFunction(final Map<String, Object> map) {

        emailService.sendEmailFunction(BizConstant.TITLE_TEMPLATE, BizConstant.CONTENT_TEMPLATE, BizConstant.PARTY_BRANCH_PROCESS_CODE, talkConfig.getSiteUrl(), map);

    }
}
